// BNF 语法规则
// <expression> ::= <term> (('+' | '-' | '=') <term>)*
// <term> ::= <factor> (('*' | '/') <factor>)*
// <factor> ::= '(' <expression> ')' | (<number> | <dot>)
// <dot> ::= <number> [.] <number>
// <number> ::= [0-9]+

function parseExpression (code) {

  code = replaceSpace(code);

  const checkResult = checkVaild(code);
  if (!checkResult.flag) {
    return checkResult;
  }

  let index = 0;

  function parseExpr () {
    let left = parseTerm();
    while (index < code.length) {
      const operator = code[ index ];
      if (operator === '+' || operator === '-' || operator === '=') {
        index++;
        const right = parseTerm();
        left = {
          type: 'expression',
          left,
          operator,
          right
        };
      } else break;
    }
    return left;
  }

  function parseTerm () {
    let left = parseFactor();
    while (index < code.length) {
      const operator = code[ index ];
      if (operator === '*' || operator === '/') {
        index++;
        const right = parseFactor();
        left = {
          type: 'term',
          left,
          operator,
          right
        };
      } else break;
    }
    return left;
  }

  let nestCnt = 0;

  function parseFactor () {
    if (code[ index ] === '(') {
      index++;
      nestCnt++;
      const content = parseExpr();
      index++;
      nestCnt--;
      return {
        type: 'parentheses',
        content,
        nestCnt: nestCnt % 3
      };
    } else {
      let left = parseNumber();
      if (code[ index ] == '.') {
        const value = code[ index ];
        index++;
        const right = parseNumber();
        left = {
          type: 'dot',
          left,
          value,
          right
        };
        if (code[ index ] === '.') throw Error('错误的小数点');
      }
      return left;
    }
  }

  function parseNumber () {
    let number = '';
    while (index < code.length && /\d/.test(code[ index ])) {
      number += code[ index++ ];
    }
    return {
      type: 'number',
      value: number
    };
  }

  function checkVaild (input) {
    if (input.length === 0) return { flag: false, reason: '空输入', index: 0 };
    const vaildChars = '0123456789()=+-/.*';
    for (let i = 0; i < input.length; i++) {
      if (vaildChars.indexOf(input[ i ]) === -1)
        return { flag: false, reason: '非法字符: ' + input[ i ], index: i };
    };
    let stack = [];
    for (let i = 0; i < input.length; i++) {
      if (input[ i ] === '(') stack.push('(');
      else if (input[ i ] === ')') {
        if (stack.at(-1) === '(') stack.pop();
        else return { flag: false, reason: '无效括号', index: i };
      }
    }
    return { flag: stack.length === 0, reason: '过多括号', index: 0 };
  }

  function replaceSpace (input) {
    let res = '';
    for (let i = 0; i < input.length; i++) {
      if (input[ i ] !== ' ') res += input[ i ];
    }
    return res;
  }

  return {
    flag: true,
    syntaxTree: parseExpr()
  };
}

const operators = {
  '+': 'addition',
  '-': 'subtraction',
  '*': 'multiplication',
  '/': 'division',
  '=': 'equality'
};

function generateHTML (node) {
  switch (node.type) {
    case 'expression':
      return `<span class="expression">
              ${generateHTML(node.left)}
              <span class="${operators[ node.operator ]}">
                ${node.operator}
              </span>
              ${generateHTML(node.right)}
           </span>`;
    case 'parentheses':
      return `<span class=parentheses_${node.nestCnt}>
                (
              </span>
                ${generateHTML(node.content)}
              <span class="parentheses_${node.nestCnt}">
                )
              </span>`;
    case 'number':
      return `<span class="number">${node.value}</span>`;
    case 'term':
      return `${generateHTML(node.left)}
              <span class="${operators[ node.operator ]}">
                ${node.operator}
              </span>
              ${generateHTML(node.right)}`;
    case 'dot':
      return `${generateHTML(node.left)}
              <span class="dot">.</span>
              ${generateHTML(node.right)}`;
    // default:
    //   return `<span class="unknow">${node.reason}</span>`;
  }
}

function debounce (func, wait) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(context, args), wait);
  };
}

const codeNode = document.getElementById("code");
const input = document.getElementById('input1');

function convert () {
  const code = input.value;

  const parseResult = parseExpression(code);
  if (parseResult.flag) {
    const highlightedCode = generateHTML(parseResult.syntaxTree);
    codeNode.innerHTML = highlightedCode;
  } else {
    codeNode.innerHTML = parseResult.reason;
  }
}

input.addEventListener('input', debounce(convert, 300));


function printAST (node) {
  if (node.type === 'expression') {
    console.log('Expression:');
    console.log('Left:');
    printAST(node.left);
    console.log(`Operator: ${node.operator}`);
    console.log('Right:');
    printAST(node.right);
  } else if (node.type === 'parentheses') {
    console.log('Parentheses:');
    printAST(node.content);
  } else if (node.type === 'number') {
    console.log(`Number: ${node.value}`);
  } else if (node.type === 'term') {
    console.log('Term:');
    console.log('Left:');
    printAST(node.left);
    console.log(`Operator: ${node.operator}`);
    console.log('Right:');
    printAST(node.right);
  }
}